<?php

/**
 * SAE 文件处理
 *
 * @author banyanCheung <banyan@ibos.com.cn>
 * @link http://www.ibos.com.cn/
 * @copyright Copyright &copy; 2012-2013 IBOS Inc
 */
/**
 * SAE文件处理类,实现SAE平台文件IO处理
 * 
 * @package ext.enginedriver.sae
 * @author banyanCheung <banyan@ibos.com.cn>
 * @version $Id: SaeFile.php 1930 2013-12-14 08:05:27Z gzhzh $
 */

namespace application\core\engines\sae;

use application\core\utils\File;
use application\core\utils\String;
use application\modules\main\model\Attachment;

class SaeFile {

    private static $_instance;

    /**
     *
     * @var array 
     */
    protected $filesInfo = array();

    /**
     * sae 所用的 domain,默认为 data
     * @var string 
     */
    private $_domain;

    /**
     * sae 封装的 storage 对象
     * @var object 
     */
    private $_storage;

    /**
     * 基本文件mime类型
     * @var array 
     */
    private $_mimeTypes = array(
        //applications
        'ai' => 'application/postscript',
        'eps' => 'application/postscript',
        'exe' => 'application/octet-stream',
        'doc' => 'application/vnd.ms-word',
        'xls' => 'application/vnd.ms-excel',
        'ppt' => 'application/vnd.ms-powerpoint',
        'pps' => 'application/vnd.ms-powerpoint',
        'pdf' => 'application/pdf',
        'xml' => 'application/xml',
        'odt' => 'application/vnd.oasis.opendocument.text',
        'swf' => 'application/x-shockwave-flash',
        // archives
        'gz' => 'application/x-gzip',
        'tgz' => 'application/x-gzip',
        'bz' => 'application/x-bzip2',
        'bz2' => 'application/x-bzip2',
        'tbz' => 'application/x-bzip2',
        'zip' => 'application/zip',
        'rar' => 'application/x-rar',
        'tar' => 'application/x-tar',
        '7z' => 'application/x-7z-compressed',
        // texts
        'txt' => 'text/plain',
        'php' => 'text/x-php',
        'html' => 'text/html',
        'htm' => 'text/html',
        'js' => 'text/javascript',
        'css' => 'text/css',
        'rtf' => 'text/rtf',
        'rtfd' => 'text/rtfd',
        'py' => 'text/x-python',
        'java' => 'text/x-java-source',
        'rb' => 'text/x-ruby',
        'sh' => 'text/x-shellscript',
        'pl' => 'text/x-perl',
        'sql' => 'text/x-sql',
        // images
        'bmp' => 'image/x-ms-bmp',
        'jpg' => 'image/jpeg',
        'jpeg' => 'image/jpeg',
        'gif' => 'image/gif',
        'png' => 'image/png',
        'tif' => 'image/tiff',
        'tiff' => 'image/tiff',
        'tga' => 'image/x-targa',
        'psd' => 'image/vnd.adobe.photoshop',
        //audio
        'mp3' => 'audio/mpeg',
        'mid' => 'audio/midi',
        'ogg' => 'audio/ogg',
        'mp4a' => 'audio/mp4',
        'wav' => 'audio/wav',
        'wma' => 'audio/x-ms-wma',
        // video
        'avi' => 'video/x-msvideo',
        'dv' => 'video/x-dv',
        'mp4' => 'video/mp4',
        'mpeg' => 'video/mpeg',
        'mpg' => 'video/mpeg',
        'mov' => 'video/quicktime',
        'wm' => 'video/x-ms-wmv',
        'flv' => 'video/x-flv',
        'mkv' => 'video/x-matroska'
    );

    /**
     * 构造函数,初始化 domain 以及 storage 对象
     * @param string $domain storage的名称
     * @param string $ak accessKey 
     * @param string $sk secretKey 
     * @return void 
     */
    public function __construct( $domain = 'data', $ak = '', $sk = '' ) {
        $this->_domain = $domain;
        $this->_storage = new \SaeStorage( $ak, $sk );
    }

    public static function getInstance( $domain = 'data', $ak = '', $sk = '' ) {
        if ( self::$_instance == null ) {
            self::$_instance = new self( $domain, $ak, $sk );
        }
        return self::$_instance;
    }

    /**
     * 获取文件列表 默认会将文件做筛选，不显示子目录的文件
     * .txt 表示搜索 所有*.txt
     * / 表示搜索 所有目录 （假设以/结尾则是目录）
     * aaa/ 表示搜索 aaa目录下的文件
     * @param string $dirName
     * @param boolean $showAll
     * @param boolean $showDir
     * @return array
     */
    public function getList( $dirName, $showAll = false, $showDir = false ) {
        if ( substr( $dirName, 0, 1 ) == DIRECTORY_SEPARATOR ) {
            $dirName = substr( $dirName, 1, strlen( $dirName ) );
        }

        $prefix = $dirName;
        $prefix = str_replace( ' ', '', $prefix );
        if ( empty( $prefix ) ) {
            $prefix = '*';
        }

        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();
        $ls = $s->getList( $domain, $prefix );
        if ( !empty( $ls ) ) {
            //排除本身
            if ( !$showAll ) {
                foreach ( $ls as $key => $one ) {
                    //不显示子目录
                    $tmp = str_replace( $ls[0], '', $one );

                    $bo = strpos( $tmp, '/' );
                    $lenth = strlen( $tmp );
                    if ( $bo > 1 && $lenth > $bo + 1 ) {
                        unset( $ls[$key] );
                    }
                }
            }
            unset( $ls[0] );

            if ( $showDir == false ) {
                foreach ( $ls as $key => $one ) {
                    if ( substr( $one, -1 ) == DIRECTORY_SEPARATOR ) {
                        unset( $ls[$key] );
                        continue;
                    }
                }
            }
        }
        return $ls;
    }

    /**
     * 获取指定目录下的所有文件名 
     * @param string $dirName 目录名
     * @param boolean $fold 为 true 则不遍历子目录
     * @return array
     *  array(
      'dirNum'=>''
      'dirNum'=>''
      'dirs'=>array()
      'files'=>array()
      );
     */
    public function getFiles( $dirName, $fold = true ) {
        $dirName = $this->formatDir( $dirName );
        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();

        $ls = $s->getListByPath( $domain, $dirName, 1000, 0, $fold );
        $rs = $ls['files'];
        if ( !empty( $rs ) ) {
            $arr = explode( "/", $dirName );
            $autoSinaName = $dirName . "/" . end( $arr );

            foreach ( $rs as $key => $tmp ) {
                $fullName = $tmp['fullName'];
                if ( substr( $fullName, -1 ) == DIRECTORY_SEPARATOR || $fullName == $autoSinaName ) {
                    unset( $rs[$key] );
                    continue;
                }
                # 和谐掉新浪自创的文件名 - -
                $mine = $this->getMimeType( $tmp['Name'] );
                if ( $tmp['length'] == 26 && empty( $mine ) ) {
                    unset( $rs[$key] );
                    continue;
                }

                $rs[$key]['fileName'] = $tmp['fullName'];
            }
        }
        return $rs;
    }

    /**
     * 获得文件夹列表
     * @param string $dirName
     * @param boolean $fold
     * @return array
     */
    public function getDirs( $dirName, $fold = true ) {
        $dirName = $this->formatDir( $dirName );
        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();
        $ls = $s->getListByPath( $domain, $dirName, 1000, 0, $fold );
        $rs = $ls['dirs'];
        if ( !empty( $rs ) ) {
            foreach ( $rs as $key => $tmp ) {
                $rs[$key]['fileName'] = $tmp['fullName'];
            }
        }
        return $rs;
    }

    /**
     * 上传文件
     * @param type $destFileName
     * @param type $srcFileName
     * @param type $attr
     * @return boolean
     */
    public function uploadFile( $destFileName, $srcFileName, $attr ) {
        if ( empty( $srcFileName ) ) {
            return false;
        }
        if ( empty( $attr['type'] ) ) {
            //根据后缀名获得文件类型
            $attr['type'] = $this->getMimeType( $destFileName );
        }
        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();
        $rs = $s->upload( $domain, $destFileName, $srcFileName, $attr );
        return $rs;
    }

    /**
     * 获得文件资料
     * 目前支持的文件属性
     *  - expires: 浏览器缓存超时，功能与Apache的Expires配置相同
     *  - encoding: 设置通过Web直接访问文件时，Header中的Content-Encoding。
     *  - type: 设置通过Web直接访问文件时，Header中的Content-Type。
     * @param string $fileName
     * <code>
     *   $attr = array (
      'type'=>'文件类型',
      'length'=>文件长度,
      'datetime'=>'添加时间'
      )
      Array (
      [fileName] => bbb/222.txt #文件名
      [length] => 1 #文件长度
      [datetime] => 1307091828 #添加时间
      [type] => text/plain #文件类型
      )
     * </code>
     * @return type
     */
    public function getFileInfo( $fileName ) {
        $info = $this->filesInfo[$fileName];
        if ( empty( $info ) ) {
            $domain = $this->getAssetsDomain();
            $s = $this->getDiskStorage();
            $info = $s->getAttr( $domain, $fileName );
            if ( !empty( $info ) && empty( $info['type'] ) ) {
                $info['type'] = $this->getMimeType( $fileName );
            }
            $this->filesInfo[$fileName] = $info;
        }

        return $info;
    }

    /**
     * 简单字符串检测是否目录
     * @param string $dirName
     * @return boolean
     */
    public function isDir( $dirName ) {
        if ( $dirName == '' || $dirName == '/' ) {
            return true;
        }
        if ( substr( $dirName, -1 ) == DIRECTORY_SEPARATOR ) {
            return true;
        }
        return false;
    }

    /**
     * 创建 storage 目录
     * @param string $dirName 目录名
     * @return boolean
     */
    public function createDir( $dirName ) {
        if ( empty( $dirName ) ) {
            return false;
        }
        $attr = array( 'type' => 'good' );
        #目录名加上 / 则为目录
        $fileName = $dirName . DIRECTORY_SEPARATOR;
        $content = 'this is a sae dir and automatic create by ibos2';
        $rs = $this->writeFile( $fileName, $content, $attr );
        return $rs;
    }

    /**
     * 创建文件
     * @param string $fileName 文件名
     * @param string $content 写入的内容
     * @return type
     */
    public function createFile( $fileName, $content = ' ' ) {
        $rs = $this->writeFile( $fileName, $content );
        return $rs;
    }

    /**
     * 写文件
     * @param string $file 文件名
     * @param string $content 写入的内容
     * @param array $attr 附加参数
     * @return boolean
     */
    public function writeFile( $file, $content = ' ', $attr = array() ) {
        if ( empty( $file ) ) {
            return false;
        }
        if ( substr( $file, 0, 1 ) == DIRECTORY_SEPARATOR ) {
            $file = substr( $file, 1, strlen( $file ) );
        }
        $file = str_replace( '//', '/', $file );

        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();
        if ( !isset( $attr['type'] ) ) {
            $attr['type'] = $this->getMimeType( $file );
        }

        $rs = $s->write( $domain, $file, $content, -1, $attr );
        return $rs;
    }

    /**
     * 复制文件
     * @param string $old 旧文件
     * @param string $new 新文件
     * @return boolean 
     */
    public function copyFile( $old, $new ) {
        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();
        //读取数据
        $attr = $s->getAttr( $domain, $old );
        $content = $this->readFile( $old );
        $exists = $this->writeFile( $new, $content, $attr );
        return $exists;
    }

    /**
     * 重命名文件
     * @param string $old 旧文件
     * @param string $new 新文件
     * @return boolean
     */
    public function renameFile( $old, $new ) {
        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();
        // 读取数据
        $attr = $s->getAttr( $domain, $old );
        $content = $this->readFile( $old );
        $exists = $this->writeFile( $new, $content, $attr );
        if ( $exists ) {
            $exists = $this->deleteFile( $old );
        }
        return $exists;
    }

    /**
     * 重命名文件夹
     * @param string $old 原来的名称
     * @param string $new 新名称
     * @return boolean
     */
    public function renameDir( $old, $new ) {
        $old = $this->formatDir( $old );
        $new = $this->formatDir( $new );
        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();
        $list = $s->getList( $domain, $old );
        if ( !empty( $list ) ) {
            $length = strlen( $old );
            foreach ( $list as $oldName ) {
                $relName = substr( $oldName, $length );
                $newName = $new . $relName;
                $exists = $this->renameFile( $oldName, $newName );
            }
        }
        return $exists;
    }

    /**
     * 移动文件
     * @param string $old 旧文件
     * @param string $new 新文件
     * @return type
     */
    public function moveFile( $old, $new ) {
        return $this->renameFile( $old, $new );
    }

    /**
     * 读取文件内容
     * @param string $file 文件名
     * @return type
     */
    public function readFile( $file ) {
        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();
        $content = $s->read( $domain, $file );
        return $content;
    }

    /**
     * 文件是否存在在storage中
     * @param string $file 文件名
     * @return boolean
     */
    public function fileExists( $file ) {
        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();
        $exists = $s->fileExists( $domain, $file );
        return $exists;
    }

    /**
     * 文件夹是否存在
     * @param string $dir 文件夹名
     * @return boolean
     */
    public function dirExists( $dir = '' ) {
        $dirName = $this->formatDir( $dir );
        $dir = $dirName . "/";
        $exists = $this->fileExists( $dir );
        return $exists;
    }

    /**
     * 清空指定目录里文件 （不包含子目录）
     * @param string $dir 指定目录
     * @return boolean
     */
    public function clearDir( $dir ) {
        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();
        $dirName = $this->formatDir( $dir );

        $list = $s->getList( $domain, $dirName );
        $exists = true;
        if ( !empty( $list ) ) {
            foreach ( $list as $one ) {
                $exists = $this->deleteFile( $one );
            }
        }
        return $exists;
    }

    /**
     * 删除文件
     * @param string $file 文件名
     * @return boolean 删除成功与否
     */
    public function deleteFile( $file ) {
        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();
        $exists = $s->delete( $domain, $file );
        return $exists;
    }

    /**
     * 文件夹是否有子目录
     * @param type $dirName
     * @return type
     */
    public function hasChildren( $dirName ) {
        $dirName = $this->formatDir( $dirName );
        $s = $this->getDiskStorage();
        $domain = $this->getAssetsDomain();
        $ls = $s->getListByPath( $domain, $dirName );
        return $ls['dirNum'] > 0;
    }

    /**
     * 根据文件名获得 mime信息
     * @param type $file
     * @return type
     */
    public function getMimeType( $file ) {
        //获得后缀名
        $hx = '';
        $extend = explode( ".", $file );
        if ( !empty( $extend ) ) {
            $va = count( $extend ) - 1;
            $hx = $extend[$va];
        }
        $type = '';
        //根据后缀名获得 mimetype
        if ( !empty( $this->_mimeTypes[$hx] ) ) {
            $type = $this->_mimeTypes[$hx];
        }
        return $type;
    }

    /**
     * 获得web访问路径
     * @param string $path 要读取的文件名
     * @return string 
     */
    public function fileName( $path = '' ) {
        $domain = $this->getAssetsDomain();
        $s = $this->getDiskStorage();
        $url = $s->getUrl( $domain, $path );
        $urls = parse_url( $url );
        if ( !isset( $urls['scheme'] ) ) {
            $url = 'http://' . $url;
        }
        return $url;
    }

    /**
     * 获取错误代码
     * @return integer
     */
    public function errmsg() {
        return $this->getDiskStorage()->errmsg();
    }

    /**
     * 格式化目录名
     * @param string $dirName 目录名
     * @return string
     */
    public function formatDir( $dirName ) {
        $dirName = trim( $dirName, DIRECTORY_SEPARATOR );
        return $dirName;
    }

    /**
     * 图形规格
     * @param string $image
     * @return array 
     */
    public function imageSize( $image ) {
        if ( !is_readable( $image ) ) {
            $sufffix = String::getFileExt( $image );
            $image = $this->fetchTemp( $image, $sufffix );
        }
        return getimagesize( $image );
    }

    /**
     * 获取新浪临时目录路径
     * @return string
     */
    public function getTempPath() {
        return SAE_TMP_PATH;
    }

    /**
     * 获取文件大小
     * @param string $file 文件名
     * @return integer
     */
    public function fileSize( $file ) {
        if ( !is_readable( $file ) ) {
            $file = $this->fetchTemp( $file );
        }
        return sprintf( '%u', filesize( $file ) );
    }

    /**
     * 读取一个网络文件到临时目录并生成临时文件
     * @param string $file 要读取的文件URL
     * @param sstring $suffix 生成临时文件的后缀，为空则自动读取
     * @return string 临时文件地址
     */
    public function fetchTemp( $file, $suffix = '' ) {
        if ( empty( $suffix ) ) {
            $suffix = pathinfo( $file, PATHINFO_EXTENSION );
        }
        $tmp = SAE_TMP_PATH . 'tmp.' . $suffix;
        $fetch = new SaeFetchurl();
        $fileContent = $fetch->fetch( $file );
        file_put_contents( $tmp, $fileContent );
        return $tmp;
    }

    public function download( $attach, $downloadInfo ) {
        $attachUrl = File::getAttachUrl();
        $attachment = File::fileName( $attachUrl . '/' . $attach['attachment'] );
        Attachment::model()->updateDownload( $attach['aid'] );
        header( "Location:{$attachment}" );
    }

    /**
     * 获得domain
     * @return string
     */
    private function getAssetsDomain() {
        return $this->_domain;
    }

    /**
     * 获得 STORAGE 对象
     * @return object
     */
    private function getDiskStorage() {
        return $this->_storage;
    }

}
